package com.yb.socket.udp;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.blankj.utilcode.util.NetworkUtils;
import com.blankj.utilcode.util.ThreadUtils;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;

/**
 * Created by zyb
 * Date 2021/10/18 13:03
 * Desc udp 工具类
 */

public class UDPUtils {

    private static final Handler mHandler = new Handler(Looper.getMainLooper());
    private static final ExecutorService mPool = ThreadUtils.getCpuPool();
    private static boolean isReceiveRunning;
    private static boolean isSendBroadcastRunning;
    private static final int TIMEOUT = 5000;  // 设置接收数据的超时时间
    private static final int RETRY_NUM = 5;     // 设置重发数据的最多次数

    /**
     * 发送udp广播
     *
     * @param port     端口
     * @param data     数据
     * @param callback 响应回调
     */
    public static void sendBroadcast(int port, String data, UDPCallback callback) {
        sendMessage(NetworkUtils.getBroadcastIpAddress(), port, data, callback);
    }

    /**
     * 重复发送udp广播
     *
     * @param port     端口
     * @param data     数据
     * @param interval 间隔时间
     * @param callback 响应回调
     */
    public static void sendBroadcast(int port, String data, int interval, UDPCallback callback) {
        isSendBroadcastRunning = true;
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                sendMessage(NetworkUtils.getBroadcastIpAddress(), port, data, callback);
                if (isSendBroadcastRunning) {
                    mHandler.postDelayed(this, interval);
                }
            }
        };
        mHandler.postDelayed(runnable, interval);
    }

    /**
     * 发送udp消息
     *
     * @param ip       ip地址
     * @param port     端口
     * @param data     数据
     * @param callback 响应数据回调
     */
    public static void sendMessage(String ip, int port, String data, UDPCallback callback) {
        mPool.execute(() -> {
            try {
                InetAddress inetAddress = InetAddress.getByName(ip);
                DatagramSocket client = new DatagramSocket();
                // 设置接收数据时阻塞的最长时间
                client.setSoTimeout(TIMEOUT);
                // 发送数据包
                byte[] dataBytes = data.getBytes();
                DatagramPacket dataPacket = new DatagramPacket(dataBytes, dataBytes.length, inetAddress, port);
                // 响应数据包
                byte[] responseBytes = new byte[1024 * 1024];
                DatagramPacket responsePacket = new DatagramPacket(responseBytes, responseBytes.length);
                int tries = 0;
                boolean receivedResponse = false;
                while (!receivedResponse && tries < RETRY_NUM) {
                    // 发送数据
                    client.send(dataPacket);
                    try {
                        // 响应数据
                        client.receive(responsePacket);
                        String serviceIp = responsePacket.getAddress().getHostAddress();
                        String response = new String(responseBytes, 0, responsePacket.getLength());
                        if (callback != null) {
                            ThreadUtils.runOnUiThread(() -> callback.call(serviceIp, response));
                        }
                        receivedResponse = true;
                    } catch (InterruptedIOException e) {
                        // 如果接收数据时阻塞超时，重发并减少一次重发的次数
                        tries += 1;
                        Log.e("udp", "client Time out," + (RETRY_NUM - tries) + " more tries...");
                    }
                }
                Log.e("udp", "client No response -- give up.");
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }


    /**
     * 接收udp消息
     *
     * @param port     端口
     * @param repeat   重复接收
     * @param response 响应数据
     * @param callback 接收数据回调
     */

    public static void receiveMessage(int port, boolean repeat, String response, UDPCallback callback) {
        isReceiveRunning = true;
        mPool.execute(() -> {
            try {
                // 接收数据
                byte[] dataBytes = new byte[1024 * 1024];
                final DatagramPacket clientPacket = new DatagramPacket(dataBytes, dataBytes.length);
                DatagramSocket service = new DatagramSocket(null);
                service.setReuseAddress(true);
                service.bind(new InetSocketAddress(port));
                do {
                    service.receive(clientPacket);
                    String clientIp = clientPacket.getAddress().getHostAddress();
                    String data = new String(dataBytes, 0, clientPacket.getLength());
                    ThreadUtils.runOnUiThread(() -> callback.call(clientIp, data));
                    // 响应数据
                    byte[] responseBytes = (response == null ? "default response" : response).getBytes();
                    DatagramPacket responsePacket = new DatagramPacket(responseBytes, responseBytes.length, clientPacket.getAddress(), clientPacket.getPort());
                    service.send(responsePacket);

                } while (repeat && isReceiveRunning);

                service.disconnect();
                service.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        });

    }

    /**
     * 关闭重复接收
     */
    public static void stopReceiveMessage() {
        isReceiveRunning = false;
    }

    /**
     * 关闭重复发送
     */

    public static void stopSendBroadcast() {
        isSendBroadcastRunning = false;
    }

    /**
     * 清除资源
     */
    public static void clear() {
        stopReceiveMessage();
        stopSendBroadcast();
        mHandler.removeCallbacksAndMessages(null);
    }

    public interface UDPCallback {

        void call(String ip, String data);
    }
}
